package com.apestech.api.condition;

import com.apestech.api.condition.Step.*;
import com.apestech.formwork.condition.ConditionUtil;
import com.apestech.formwork.xml.XmlQuery;
import com.apestech.framework.db.SqlSession;
import com.apestech.framework.json.JSON;
import com.apestech.framework.json.JSONArray;
import com.apestech.framework.json.JSONObject;
import com.apestech.framework.util.callback.SpiltStrCallback;
import com.apestech.framework.xml.condition.modul.ConditionStrategy;
import com.apestech.framework.xml.plugin.XmlPaste;

import java.sql.Connection;
import java.util.ArrayList;

/**
 * @author hup
 * @create 2019-02-28-16:47
 */
public class ConditionStrategyExec extends StepUtil {
    private String conditionStrategyId;
    private ConditionStrategy conditionStrategy;
    private ArrayList<ConditionStrategy.Step> steps;
    private int stepSize;
    private JSONObject inputMap;
    private JSONObject resultMap;
    private String prefix="";
    private ArrayList<String> conList=new ArrayList<String>();
    private XmlPaste xmlPaste;
    private SqlSession session;
    private String minInput;
    private JSONArray stepInfo=new JSONArray();

    public ConditionStrategyExec(SqlSession session, String conditionStrategyId) throws Exception {
        this.session=session;
        if(this.session.getConnection()==null){
            Connection cn=this.session.getDataSource().getConnection();
            this.session.setConnection(cn);
        }
        if(conditionStrategyId.indexOf(".")>=0){
            xmlPaste=new XmlPaste(conditionStrategyId, "conditionStrategyDef", this.session);
        }
        this.conditionStrategyId=conditionStrategyId;
        init();
    }

    public ConditionStrategyExec(XmlQuery xmlQuery, String conditionStrategyId) throws Exception {
        if(conditionStrategyId.indexOf(".")>=0){
            xmlPaste=new XmlPaste(conditionStrategyId, "conditionStrategyDef", xmlQuery);
        }
        this.session=xmlQuery.getSession();
        this.conditionStrategyId=conditionStrategyId;
        init();
    }

    public SqlSession getSession() {
        return session;
    }

    public void setInput(JSONObject input) throws Exception {
        if(input==null) throw new Exception("请传入类型为JSONObject的入参，不允许为空！");
        inputMap=new JSONObject(input);
    }

    private void init() throws Exception {
        if(xmlPaste==null)
            conditionStrategy=ConditionUtil.getConditionStrategy(conditionStrategyId, session);
        else {
            xmlPaste.expain();
            conditionStrategy=xmlPaste.getConditionStrategy();
        }
        if(conditionStrategy==null) throw new Exception("请输入合法的策略执行ID["+conditionStrategyId+"]！");
        steps=conditionStrategy.getSteps();
        stepSize=steps.size();
        int pos=conditionStrategyId.lastIndexOf(".");
        if(pos>=0) prefix=leftStr(conditionStrategyId, pos+1);
        JSONObject param=iniStep(steps);
        conList=param.getKeylist();
    }

    private JSONObject iniStep(ArrayList<ConditionStrategy.Step> steps) throws Exception {
        JSONObject param=new JSONObject();
        int len=steps.size();
        for(int i=0; i<len; i++){
            ConditionStrategy.Step step=steps.get(i);
            String id=step.getId();
            try{
                if(id!=null && id.indexOf(".")<0) id=prefix+id;
                switch (step.getType()) {
                    case "condition":{
                        if(step.getId()==null) throw new Exception("条件策略必须配置id属性");
                        ConditionExec exec;
                        if(xmlPaste==null)
                            exec=new ConditionExec(session, id);
                        else exec=new ConditionExec(xmlPaste.getXmlQuery(), id);
                        StepCondition condition=new StepCondition(step, exec);
                        step.setStepLife(condition);
                        param.putAll(step.getStepLife().getMinInput());
                        break;
                    }
                    case "conditionStrategy":{
                        if(step.getId()==null) throw new Exception("执行过程必须配置id属性");
//                        if(step.getInputfields()==null) throw new Exception("入参必须配置inputfields属性");
                        ConditionStrategyExec conStrategy;
                        if(xmlPaste==null)
                            conStrategy=new ConditionStrategyExec(session, id);
                        else conStrategy=new ConditionStrategyExec(xmlPaste.getXmlQuery(), id);
                        StepConditionStrategy stepConstragegy=new StepConditionStrategy(step, conStrategy, session);
                        step.setStepLife(stepConstragegy);
                        param.putAll(step.getStepLife().getMinInput());
                        break;
                    }
                    case "queryid":{
                        if(step.getId()==null) throw new Exception("查询步骤必须配置id属性");
                        if(step.getInputfields()==null) throw new Exception("查询步骤必须配置inputfields属性");
                        StepQuery queryId=new StepQuery(step, session);
                        step.setStepLife(queryId);
                        param.putAll(step.getStepLife().getMinInput());
                        break;
                    }
                    case "sql":{
                        if(step.getSql()==null) throw new Exception("查询步骤必须配置id属性");
                        StepQuery query=new StepQuery(step, session);
                        step.setStepLife(query);
                        param.putAll(step.getStepLife().getMinInput());
                        break;
                    }
                    case "xls":{
                        if(step.getId()==null) throw new Exception("XLS读取必须配置id属性");
                        if(step.getXlsReadFile()==null) throw new Exception("XLS读取必须配置xlsReadFile属性");
                        StepXlsRead xls=new StepXlsRead(step, session);
                        step.setStepLife(xls);
                        param.putAll(step.getStepLife().getMinInput());
                        break;
                    }
                    case "while":{
                        param.putAll(iniStep(step.getFwhile().getStep()));
                        ConditionStrategy.Step wstep=step.getFwhile().getSql();
                        StepWhile pwhile=new StepWhile(wstep, session);
                        wstep.setStepLife(pwhile);
                        param.putAll(wstep.getStepLife().getMinInput());
                        break;
                    }
                    case "if":{
                        param.putAll(iniStep(step.getFif().getFthen().getThen()));
                        if(step.getFif().getEthen()!=null){
                            param.putAll(iniStep(step.getFif().getEthen().getThen()));
                        }
                        if(step.getFif().getFif()!=null){
                            param.putAll(iniStep(step.getFif().getFif().getFthen().getThen()));
                            if(step.getFif().getFif().getEthen()!=null){
                                param.putAll(iniStep(step.getFif().getFif().getEthen().getThen()));
                            }
                        }
                        break;
                    }
                    default:{
                        StepField field=new StepField(step, session);
                        step.setStepLife(field);
                        param.putAll(step.getStepLife().getMinInput());
                        break;
                    }
                }
            }catch (Exception e){
                throw new Exception("步骤["+step.getId()+"]初始化失败！请检查它的定义合法性！");
            }
        }
        return param;
    }

    public void expain(JSONObject input) throws Exception {
        inputMap=input;
        expain();
    }

    public void expain() throws Exception {
        JSONObject concurrency=null;
        boolean bdel=true;
        try{
            if(minInput!=null && !"".equals(minInput)){
                execSplitStr(minInput,",", new SpiltStrCallback(){
                    @Override
                    public void exec(String id) throws Exception {
                        if(inputMap.get(id)==null){
                            throw new Exception("请检查输小输入项！\r\n"+minInput);
                        }
                    }
                });
            }
            resultMap=new JSONObject();
            resultMap.putAll(inputMap);
            if(conditionStrategy.isbConcurrency()){
                String billField=conditionStrategy.getBillField();
                String billId=resultMap.getString(billField);
                if(billId==null){
                    throw new Exception("并发控制，要求单号字段["+billField+"]为必输项");
                }
                concurrency=new JSONObject();
                concurrency.put("BILL_ID", billId);
                concurrency.put("CON_STR_ID", conditionStrategyId);
                if(!session.getForResultSet().queryIsEmpty("SELECT 1 TS FROM EXE_CONCURRENCY WHERE BILL_ID=#{BILL_ID} AND CON_STR_ID=#{CON_STR_ID} ", concurrency)){
                    bdel=false;
                    return;
                } else {
                    session.insertTable_T("EXE_CONCURRENCY", concurrency);
                }
            }

//            resultMap=execStep(steps, resultMap, null);
            ConditionStrategyStepExec stepexec=new ConditionStrategyStepExec(
                    steps, resultMap, null, this);
            stepexec.execStep();
            resultMap=stepexec.getOutput();
            //stepInfo=stepexec.getStepInfoList();
            //System.out.println(JSON.toJSONString(stepInfo));
        } catch (Exception e){
            //System.out.println(JSON.toJSONString(stepInfo));
            throw e;
        } finally {
            if(bdel && conditionStrategy.isbConcurrency()){
                session.update_T("DELETE EXE_CONCURRENCY WHERE BILL_ID=#{BILL_ID} AND CON_STR_ID=#{CON_STR_ID} ", concurrency);
            }
        }
    }

    private JSONObject execStep(ArrayList<ConditionStrategy.Step> steps, JSONObject resultMap1, ConditionStrategy.Step pstep) throws Exception {
        int len=steps.size();  //stepSize
        for(int i=0; i<len; i++){
            ConditionStrategy.Step step=steps.get(i);
            if(step.isbPrint()){
                printMsg("执行入参："+JSON.toJSONString(resultMap1), true);
            }
            printMsg("第"+(i+1)+"步："+step.getId()+"-"+step.getName());
            String id=step.getId();
            if(id!=null && id.indexOf(".")<0) id=prefix+id;

            String sid=step.getId();
            JSONObject j=new JSONObject();
            if(pstep!=null){
                step.setPstep(pstep);
            }
            if(step.getPstep()!=null){
                j.putAll(step.getPstep().getSuperInput());
            }
            j.putAll(resultMap1);
            step.setSuperInput(j);

//            if("field".equals(step.getType())){
//                if(step.getNullValue()==null){
//                    if(inputMap.get(sid)!=null) {
//                        inputMap.remove(sid);
//                    }
//                    if(resultMap.get(sid)!=null) {
//                        resultMap.remove(sid);
//                    }
//                    if(resultMap1.get(sid)!=null) {
//                        resultMap1.remove(sid);
//                    }
//                }
//            }

            switch (step.getType()) {
                case "if":{
                    Object r=doExpression(step.getFif().getExp(), resultMap1);
                    if(castToBoolean(r)){
                        execStep(step.getFif().getFthen().getThen(), step.getSuperInput(), step);  //resultMap1
                        if(step.getFif().getFthen().isbBreak()){
                            i=len;
                            if(step.getFif().getFthen().getMsg()!=null){
                                resultMap1.put("sys_msg",step.getFif().getFthen().getMsg());
                            }
                            resultMap1.put("sys_break","1");
                        }
                    } else {
                        if(step.getFif().isbElse()){
                            if(step.getFif().getFif()!=null){
                                String sexp=step.getFif().getFif().getExp();
                                if(sexp!=null){
                                    r=doExpression(sexp, resultMap1);
                                } else {
                                    r=true;
                                }
                                if(castToBoolean(r)){
                                    execStep(step.getFif().getFif().getFthen().getThen(), step.getSuperInput(), step);  //resultMap1
                                    if(step.getFif().getFif().getFthen().isbBreak()){
                                        i=len;
                                        if(step.getFif().getFif().getFthen().getMsg()!=null){
                                            resultMap1.put("sys_msg",step.getFif().getFif().getFthen().getMsg());
                                        }
                                        resultMap1.put("sys_break","1");
                                    }
                                }
                            } else {
                                execStep(step.getFif().getEthen().getThen(), step.getSuperInput(), step);  //resultMap1
                            }
                        }
                    }
                    break;
                }
                case "while":{
                    ConditionStrategy.Step wstep=step.getFwhile().getSql();
                    StepWhile fwhile= (StepWhile) wstep.getStepLife();
                    fwhile.setInput(getAllInput(inputMap, resultMap, resultMap1));
                    String resultKey=step.getFwhile().getResultKey();
                    JSONArray list=new JSONArray();
                    String type=wstep.getType();  //sql、queryid、xls、array、json
                    if("json".equals(type) || "array".equals(type)) {
                        fwhile.setResult(step.getFwhile().getReslut());
                        fwhile.setOut(resultMap1);
                    }
                    step.getFwhile().setReslut(resultMap1);
                    ConditionStrategy.Step finalStep=step;
                    fwhile.setArrayCallback(new ArrayCallback() {
                        @Override
                        public JSONObject getRow(JSONObject row) throws Exception {
                                //finalStep.setResultMap(j);
                            JSONObject json=new JSONObject();
                            json.putAll(finalStep.getFwhile().getReslut());
                            json.putAll(row);
                            //if("json".equals(type) || "array".equals(type)) {
                                row=execStep(finalStep.getFwhile().getStep(), json, finalStep);//row
                            //} else {
                            //    row=execStep(finalStep.getFwhile().getStep(), row, null);//row
                            //}
                            return row;
                        }
                    });
                    fwhile.exec();
                    if("json".equals(type) || "array".equals(type)) {
                        list=fwhile.getList();
                        resultMap1=fwhile.getOut();
                    }
                    if(resultKey!=null){
                        resultMap1.put(resultKey, list);
                    }
                }
                default: {  //conditionStrategy queryid sql  =>default
                    if(step.getStepLife()!=null){
                        if(step.getOther()!=null && step.getOther().size()>0){
                            if(step.getOther().get("SYS_NO_CHECK1")!=null){
                                step.getOther().put("SYS_NO_CHECK", step.getOther().get("SYS_NO_CHECK1"));
                                step.getOther().remove("SYS_NO_CHECK1");
                            }
                            String SYS_NO_CHECK1=inputMap.getString("SYS_NO_CHECK");
                            String SYS_NO_CHECK2=step.getOther().getString("SYS_NO_CHECK");
                            if(SYS_NO_CHECK1!=null){
                                if(SYS_NO_CHECK2!=null){
                                    if(!SYS_NO_CHECK1.equals(SYS_NO_CHECK2)){
                                        SYS_NO_CHECK1=SYS_NO_CHECK1+","+SYS_NO_CHECK2;
                                    }
                                }
                            } else {
                                SYS_NO_CHECK1=SYS_NO_CHECK2;
                            }
                            if(SYS_NO_CHECK1!=null){
                                inputMap.put("SYS_NO_CHECK",SYS_NO_CHECK1);
                                resultMap.remove("SYS_NO_CHECK");
                                resultMap1.remove("SYS_NO_CHECK");
                                step.getOther().put("SYS_NO_CHECK1", step.getOther().get("SYS_NO_CHECK"));
                                step.getOther().remove("SYS_NO_CHECK");
                            }
                        }
                        String mInput=step.getStepLife().showMinInput();
                        if(mInput!=null){
                            printMsg("步骤最小输入项:"+step.getStepLife().showMinInput());
                        }
                        JSONObject j1=new JSONObject();
                        if(step.getPstep()!=null){
                            j1.putAll(step.getPstep().getSuperInput());
                        }
                        j1.putAll(step.getSuperInput());
                        step.getStepLife().setInput(j1);
                        //step.getStepLife().setInput(getAllInput(step.getSuperInput(), resultMap1, null));
//                        if(pstep!=null){
//                            step.getStepLife().setInput(getAllInput(inputMap, resultMap1, resultMap));
//                        } else {
//                            step.getStepLife().setInput(getAllInput(inputMap, resultMap, resultMap1));
//                        }
                        step.getStepLife().exec();
                        System.out.println(JSON.toJSONString(step.getResultMap()));
                        resultMap1.putAll(step.getSuperInput());
                        resultMap1.putAll(step.getResultMap());
                    }
                    break;
                }
            }
            if(step.getStepLife()!=null){
                boolean isbreak=step.isIsbreak();
                boolean sysbreak=step.getResultMap()==null;
                if(!sysbreak){
                    sysbreak="1".equals(step.getResultMap().getString("sys_break"));
                }
                if(isbreak && sysbreak){
                    throw new Exception(step.getName()+"["+step.getId()+"]"+"无符合条件的返回数据！\r\n"+getNoFoundMsg(step));
                }
                step.getStepLife().setConditionStrategyVar(conditionStrategy, resultMap);
                step.getStepLife().setOutput();
                resultMap.putAll(step.getStepLife().getResultMap());
            }
        }
        return resultMap1;
    }

    private void addCondition(String field, Object value, JSONObject map) throws Exception {
        //SYS_RQ
        //START_DATE<=2020-7-11 13:00  AND END_DATE>=2020-7-11 13:00
        JSONArray list= (JSONArray) map.get("condition");
        if(list==null) list=new JSONArray();
        JSONObject m=new JSONObject();
        m.put("field", field);
        m.put("value", value);
        if(field.equals("CLASSIFY_ID")){
            m.put("operator", "TREE");
        } else if(field.equals(session.getSpecialField().getStartDate())){
            m.put("operator", "LTE");
        } else if(field.equals(session.getSpecialField().getEndDate())){
            m.put("operator", "GTE");
        }
        list.add(m);
        map.put("condition", list);
        map.put(field,value);
    }

    public ArrayList<String> getAllInput() throws Exception {
        ArrayList<String> flist=new ArrayList<String>();
        for(int i=0; i<stepSize; i++) {
            ConditionStrategy.Step step = steps.get(i);
            System.out.println("第" + (i + 1) + "步：" + step.getId() + "-" + step.getName());
            String mustInput = step.getMustInput();
            System.out.println(mustInput);
            execSplitStr(mustInput, ";", new SpiltStrCallback() {
                @Override
                public void exec(String id) throws Exception {
                    if(!flist.contains(id)){
                        flist.add(id);
                    }
                }
            });
        }
        JSONObject m=conditionStrategy.getMust_input();
        ArrayList<String> klist=m.getKeylist();
        int len=klist.size();
        for(int i=0; i<len; i++){
            String k=klist.get(i);
            JSONObject map= (JSONObject) m.get(k);
            if(map.getString("key")!=null){
                k=k+"#"+map.getString("key")+"#"+map.getString("name");
            }
            if(!flist.contains(k)){
                flist.add(k);
            }
        }
        JSONObject inputs=conditionStrategy.getMust_input();
        System.out.println(JSON.toJSONString(inputs));
        return flist;
    }

    public void getInputInfo() throws Exception {
        JSONObject map=new JSONObject();
        JSONObject min=new JSONObject();
        final String[] smin = {""};
        for(int i=0; i<stepSize; i++){
            ConditionStrategy.Step step=steps.get(i);
            System.out.println("第"+(i+1)+"步："+step.getId()+"-"+step.getName());
            String id=step.getId();
            if(id!=null && id.indexOf(".")<0) id=prefix+id;
            String mustInput=step.getMustInput();
            switch (step.getType()) {
                case "condition":{
                    ConditionExec exec= ((StepCondition)step.getStepLife()).getCondition();
                    String outInput=exec.getEmptyResultStr();
                    String s=mustInput;
                    if(outInput!=null && !"".equals(outInput)) s=outInput; //s=s+","+outInput;
                    if(step.isIsbreak()){
                        execSplitStr(exec.getMinInput(), ",", new SpiltStrCallback(){
                            @Override
                            public void exec(String id) throws Exception {
                                if((","+ smin[0] +",").indexOf(","+id+",")<0){
                                    if(map.get(id)==null){
                                        if(!"".equals(smin[0])) smin[0] = smin[0] +",";
                                        smin[0] = smin[0] +id;
                                        min.put(id,"0");
                                    }
                                }
                            }
                        });
                    }
                    execSplitStr(s, ",", new SpiltStrCallback(){
                        @Override
                        public void exec(String id) throws Exception {
                            map.put(id,"0");
                        }
                    });
                    System.out.println(" ID:["+step.getId()+"]\n INPUT:["+mustInput+"]\n OUTPUT:["+outInput+"]");
                    break;
                }
//                case "queryid":{
//                    JSONArray qlist=session.getForJSON().queryId(id, getInputParamFromStr(step.getMustInput(), inputMap, step.isIsbreak()));
//                    break;
//                }
//                case "sql":{
//                    inputMap.put("autoCompletion","0");
//                    JSONArray qlist=session.getForJSON().query(id, getInputParamFromStr(step.getMustInput(), inputMap, step.isIsbreak()));
//                    inputMap.remove("autoCompletion");
//                    break;
//                }
                case "field":{
                    map.put(step.getId(),"0");
                    System.out.println(" OUTPUT:["+step.getId()+"]");
                }
            }
            String exp = null;
            if(step.getExpress()!=null){
                exp=step.getExpress();
            }
            if(step.getResult()!=null){
                exp=step.getResult();
            }
//            if(exp!=null && step.isIsbreak()){
//                Expression expression=new Expression(exp);
//                String[] list=expression.getExpressVarName();
//                step.setExpression(expression);
//                int jlen=list.length;
//                for(int j=0; j<jlen; j++){
//                    String s=list[j];
//                    if((","+ smin[0] +",").indexOf(","+s+",")<0){
//                        if(map.get(s)==null){
//                            if(!"".equals(smin[0])) smin[0] = smin[0] +",";
//                            smin[0] = smin[0] +s;
//                            min.put(id,"0");
//                        }
//                    }
//                }
//            }
        }
        minInput=smin[0];
//        System.out.println("最小输入项："+smin[0]);
    }

    public JSONObject getResultMap() {
        JSONObject in=new JSONObject();
        in.putAll(inputMap);
        in.putAll(resultMap);
        return in;
    }

    public ArrayList<String> getConList() {
        return conList;
    }

    public void setConList(ArrayList<String> conList) {
        this.conList = conList;
    }

    public ConditionStrategy getConditionStrategy() {
        return conditionStrategy;
    }

    public void addStepInfo(JSONObject map){
        stepInfo.add(map);
    }

    public JSONArray getStepInfo() {
        return stepInfo;
    }

}

